﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Zo.Common;

namespace Zo.Events
{
    /// <summary>
    /// 事件处理器。
    /// </summary>
    public interface IEventHandler
    {
        /// <summary>
        /// 事件名。
        /// </summary>
        string Name { get; }

        /// <summary>
        /// 事件处理。
        /// </summary>
        /// <param name="envelope">事件。</param>
        /// <returns>处理结果。</returns>
        IEventResult Handle(object envelope);
    }

    /// <summary>
    /// 事件处理器。
    /// </summary>
    /// <typeparam name="TEvent">事件类型。</typeparam>
    public interface IEventHandler<TEvent> : IEventHandler
    {
        /// <summary>
        /// 事件处理。
        /// </summary>
        /// <param name="envelope">事件。</param>
        /// <returns>处理结果。</returns>
        IEventResult Handle(TEvent envelope);
    }

    /// <summary>
    /// 事件处理器基类。
    /// </summary>
    /// <typeparam name="TEvent"></typeparam>
    public abstract class EventHandlerBase<TEvent> : IEventHandler<TEvent>
    {
        public string Name => typeof(TEvent).Name.ToLower();

        /// <summary>
        ///
        /// </summary>
        /// <param name="envelope"></param>
        /// <returns></returns>
        public IEventResult Handle(TEvent envelope)
        {
            return OnHandle(envelope);
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="envelope"></param>
        /// <returns></returns>
        public IEventResult Handle(object envelope)
        {
            return Handle((TEvent)envelope);
        }

        /// <summary>
        /// 发送异步事件，不等待处理结果。
        /// </summary>
        /// <param name="envelope">事件。</param>
        protected virtual void SendEvent(object envelope)
        {
            EventManager.Instance.Handle(envelope);
        }

        /// <summary>
        /// 发送异步事件，等待处理结果。
        /// </summary>
        /// <param name="envelope">事件。</param>
        /// <returns>事件的处理结果。</returns>
        protected virtual IEventResult HandleEvent(object envelope)
        {
            return HandleEventWhenAll(envelope).FirstOrDefault() ?? new EventResult();
        }

        /// <summary>
        /// 发送一批事件，并等待所有事件处理结果。
        /// </summary>
        /// <param name="envelopes">事件集合。</param>
        /// <returns>所有事件的处理结果。</returns>
        protected virtual IEnumerable<IEventResult> HandleEventWhenAll(params object[] envelopes)
        {
            if (envelopes == null || envelopes.Length <= 0)
                return Enumerable.Empty<IEventResult>();

            return Task.WhenAll(envelopes.Select(envelope => EventManager.Instance.HandleAsync(envelope)).ToArray()).GetAwaiter().GetResult();
        }

        /// <summary>
        /// 异步并行处理一批 actions，并等待处理结束。
        /// </summary>
        /// <param name="millisecondsTimeout">等待超时时间（单位：毫秒）</param>
        /// <param name="actions">处理方法。</param>
        /// <returns>True：处理结束，False：等待超时。</returns>
        protected virtual bool TaskHandleWhenAll(int millisecondsTimeout = 2000, params Action[] actions)
        {
            return Task.WhenAll(actions.Select(Task.Run).ToArray()).Wait(millisecondsTimeout);
        }

        /// <summary>
        /// 异步并行处理一批 actions，等待并返回结果。
        /// </summary>
        /// <typeparam name="TSource">类型。</typeparam>
        /// <param name="actions">处理方法。</param>
        /// <returns>返回结果集合。</returns>
        protected virtual IEnumerable<TSource> TaskHandleWhenAll<TSource>(params Func<TSource>[] actions)
        {
            return Task.WhenAll(actions.Select(Task.Run).ToArray()).GetAwaiter().GetResult();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="_event"></param>
        /// <returns></returns>
        protected abstract IEventResult OnHandle(TEvent _event);
    }

    internal class DefaultEventHandler<TEvent> : IEventHandler
    {
        private readonly IEventHandler<TEvent> _eventHandler;

        public DefaultEventHandler(string name, IEventHandler<TEvent> eventHandler)
        {
            Check.NotEmpty(name, nameof(name));
            Check.NotNull(eventHandler, nameof(eventHandler));

            Name = name ?? string.Empty;
            _eventHandler = eventHandler;
        }

        public string Name { get; }

        public IEventResult Handle(object envelope)
        {
            return _eventHandler.Handle((TEvent)envelope);
        }
    }
}